package com.flipdf.viewer;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;

import com.flipdf.viewer.stamp.Stamp;
import com.sun.pdfview.PDFPage;

public class PagePanel extends JPanel implements Scrollable, MouseListener, MouseMotionListener {

	private int maxUnitIncrement = 1;

	private File owner;
	private Rectangle2D clip;
	private Rectangle2D prevClip;
	private Dimension prevSize;
	private AffineTransform currentXform;
	private Image currentImage;
	private PDFPage currentPage;

	private float ratio = 1.0F;

	List<Stamp> stamps;
	
	private Point pressPoint;
	private boolean isPressInStamp;
	private Stamp selectedStamp;
	
	public PagePanel(File owner) {
		this.owner = owner;
		this.setPreferredSize(new Dimension(600, 800));
		stamps = new ArrayList<Stamp>();
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
	}

	public void paint(Graphics g) {
		Dimension size = getSize();
		g.setColor(Color.DARK_GRAY);
		g.fillRect(0, 0, getWidth(), getHeight());
		if (currentImage == null) {
			g.setColor(Color.black);
			g.drawString("No page selected", getWidth() / 2 - 30, getHeight() / 2);
		} else {
			// draw the image
			int imwid = currentImage.getWidth(null);
			int imhgt = currentImage.getHeight(null);

			// draw it centered within the panel
			int offx = (size.width - imwid) / 2;
			int offy = (size.height - imhgt) / 2;

			g.drawImage(currentImage, offx, offy, this);
		}

		Graphics2D g2 = (Graphics2D) g;
		g2.transform(AffineTransform.getScaleInstance(ratio, ratio));
		for (Stamp s : stamps) {
			s.draw(g2);
		}
	}

	public void showPage(PDFPage page) {
		if (currentPage != null && prevSize != null) {
			currentPage.stop(prevSize.width, prevSize.height, prevClip);
		}

		currentPage = page;
		if (page == null) {
			currentImage = null;
			clip = null;
			currentXform = null;
			repaint();
			return;
		}

		Dimension size = this.getPreferredSize();
		if (size.width == 0 || size.height == 0)
			return;

		Rectangle2D useClip = clip;
		if (clip != null && currentXform != null) {
			useClip = currentXform.createTransformedShape(clip).getBounds2D();
		}

		// Dimension pageSize = page.getUnstretchedSize(size.width, size.height,
		// useClip);
		Dimension pageSize = new Dimension((int) (page.getBBox().getWidth() * ratio), (int) (page
				.getBBox().getHeight() * ratio));
		this.setPreferredSize(pageSize);
		// currentImage = page.getImage(pageSize.width, pageSize.height,
		// useClip, this);
		currentImage = page.getImage(pageSize.width, pageSize.height, page.getBBox(), this);
		currentXform = page.getInitialTransform(pageSize.width, pageSize.height, useClip);
		try {
			currentXform = currentXform.createInverse();
		} catch (NoninvertibleTransformException nte) {
			System.out.println("Error inverting page transform!");
			nte.printStackTrace();
		}

		prevClip = useClip;
		prevSize = pageSize;

		repaint();
	}

	public void zoom(float ratio) {
		if (this.ratio == ratio)
			return;

		if (currentPage != null) {
			this.ratio = ratio;
			showPage(currentPage);
		}
	}

	public void addStamp(Stamp stamp) {
		this.stamps.add(stamp);
		repaint();
	}
	
	public List<Stamp> getStamps() { 
		return this.stamps;
	}
	
	public File getOwner() {
		return this.owner;
	}

	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
		// Get the current position.
		int currentPosition = 0;
		if (orientation == SwingConstants.HORIZONTAL) {
			currentPosition = visibleRect.x;
		} else {
			currentPosition = visibleRect.y;
		}

		// Return the number of pixels between currentPosition
		// and the nearest tick mark in the indicated direction.
		if (direction < 0) {
			int newPosition = currentPosition - (currentPosition / maxUnitIncrement)
					* maxUnitIncrement;
			return (newPosition == 0) ? maxUnitIncrement : newPosition;
		} else {
			return ((currentPosition / maxUnitIncrement) + 1) * maxUnitIncrement - currentPosition;
		}
	}

	public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
		if (orientation == SwingConstants.HORIZONTAL) {
			return visibleRect.width - maxUnitIncrement;
		} else {
			return visibleRect.height - maxUnitIncrement;
		}
	}

	public boolean getScrollableTracksViewportWidth() {
		return false;
	}

	public boolean getScrollableTracksViewportHeight() {
		return false;
	}

	public void setMaxUnitIncrement(int pixels) {
		maxUnitIncrement = pixels;
	}

	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mousePressed(MouseEvent e) {

	}

	public void mouseReleased(MouseEvent e) {
		Point p = e.getPoint();
		if (isPressInStamp) {
			selectedStamp.move(p.x - pressPoint.x, p.y - pressPoint.y);
		}
		isPressInStamp = false;
		repaint();
	}

	public void mouseDragged(MouseEvent e) {
		Point p = e.getPoint();
		Point sp = new Point((int)(p.x / ratio), (int)(p.y / ratio));
		for (Stamp s : stamps) {
			if (s.containPoint(sp)) {
				pressPoint = p;
				isPressInStamp = true;
				selectedStamp = s;
			}
		}
	}

	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub
	}
}
